<?php
/**
 * Date: 2024.8
 * Author: kahn
 */
namespace service\vip;

use config\Order as OrderConfig;
use config\Response;
use models\vip\IapReceiptModel;
use service\vip\payment\AlipayService;
use service\vip\payment\ApplepayService;
use service\vip\payment\WxpayService;
use WeChat\Exceptions\InvalidResponseException;
use WeChat\Exceptions\LocalCacheException;
use models\vip\GoodsModel;
use models\vip\OrderModel;
use models\vip\SaleModel;

class OrderService extends \Service{

    const MIN_PRICE = 0.01;

    public static function create($userId, $goodsId, $saleId): int | array
    {
        $goods = GoodsModel::findOrEmpty($goodsId);
        if($goods->isEmpty() || $goods->status == 0){
            return Response::SYSTEM_ERROR;
        }
        $discount = 0;
        if($saleId != 0){
            $sale = SaleModel::findOrEmpty($saleId);
            if(!VipService::saleCanUse($sale, $goodsId)){
                return Response::SYSTEM_ERROR;
            }
            $discount = VipService::_discount($sale['type'], $sale['discount'], $goods['price']);
        }
        $price = $goods['price'] - $discount;
        if($price < self::MIN_PRICE){
            return Response::LESS_THAN_MIN_PRICE;
        }

        $row['userId'] = $userId;
        $row['goodsId'] = $goodsId;
        $row['title'] = $goods['title'];
        $row['saleId'] = $saleId;
        $row['discount'] = $discount;
        $row['price'] = $price;

        $order = OrderModel::create($row);
        if($order->isEmpty()){
            return Response::SYSTEM_ERROR;
        }

        return [$order->id, $price];
    }

    private static function _sn(): string
    {
        return 'no'.date('YmdHis').rand(1000,9999);
    }


    /*
     * 支付
     */
    /**
     * @throws InvalidResponseException
     * @throws LocalCacheException
     */
    static public function pay($userId, $orderId, $payment, $payWay, $openId): ?array
    {
        $order = OrderModel::field('id,userId,title,price,status')
            ->findOrEmpty($orderId);

        if($order->isEmpty()
            || $userId != $order->userId
            || ($order->status != OrderConfig::ORDER_STATUS_CREATE && $order->status != OrderConfig::ORDER_STATUS_PAYING)
            || $order->deleted == OrderConfig::ORDER_DELETED_YES
        ){
            \Log::error("order error on pay orderId:$orderId, userId:$userId", $order->toArray());
            return null;
        }

        //延迟到支付时生成sn, 防止用户终止支付, 再次支付该订单时, 支付机构提示订单重复
        $sn = self::_sn();

        $res = [];
        switch($payment) {
            case OrderConfig::ORDER_PAYMENT_ALIPAY:
                $res = (new AlipayService())->pay($payWay, $order['price'], $sn, $order['title']);
                break;
            case OrderConfig::ORDER_PAYMENT_WXPAY:
                $res = (new WxpayService())->pay($payWay, $order['price'], $sn, $order['title'], $openId);
                break;
            case OrderConfig::ORDER_PAYMENT_IAP:
                $res = (new ApplepayService())->pay($sn);
                break;
        }
        if(!$res){
            \Log::error("pay call third platform pay error  $userId, $orderId, $payment, $payWay, $openId");
            return null;
        }
        $order->sn = $sn;
        $order->payment = $payment;
        $order->status = OrderConfig::ORDER_STATUS_PAYING;

        $ret = $order->save();
        if(!$ret){
            \Log::error("update order on pay orderId:$orderId, userId:$userId", $order->toArray());
            return null;
        }
        return $res;
    }

    /**
     * @param $sn
     * @param $price
     * @param $tradeno
     * @param $paidTime
     * @param callable|null $func
     * @return bool
     */
    public static function deal($sn, $price, $tradeno, $paidTime, ?callable $func=null): bool
    {
        $order = OrderModel::where('sn', $sn)->findOrEmpty();
        if ($order->isEmpty()
            || $order->status != OrderConfig::ORDER_STATUS_PAYING
            || $order->deleted == OrderConfig::ORDER_DELETED_YES
            || $order->price != $price
        ){
            \Log::error("get order on paid error sn:$sn, tradeno:$tradeno paidTime:$paidTime", $order->toArray());
            return false;
        }

        $order->startTrans();
        try{
            $ret = VipService::createVip($order['userId'], $order['goodsId']);
            if(!$ret){
                $order->rollback();
                return false;
            }

            $order->status = OrderConfig::ORDER_STATUS_PAID;
            $order->paidTime = $paidTime;
            $order->tradeno = $tradeno;
            if(!$order->save()){
                \Log::error("save order on paid error sn:$sn, tradeno:$tradeno paidTime:$paidTime", $order->toArray());
                $order->rollback();
                return false;
            }

            if($func && is_callable($func) && !$func()){
                $order->rollback();
                return false;
            }
            $order->commit();  //提交事务
            return true;
        }catch (\Exception $e){
            \Log::error(" exception on paid sn:$sn, tradeno:$tradeno paidTime:$paidTime", $order->toArray());
            $order->rollBack();
            return false;
        }
    }

    /**
     * @desc 查询订单状态
     */
    static public function query($userId, $orderId): ?int
    {
        $order = OrderModel::findOrEmpty($orderId);
        if($order->isEmpty()
            || $order->userId != $userId
            || $order->deleted == OrderConfig::ORDER_DELETED_YES
        ){
            \Log::error("error on query order userId:$userId, orderId:$orderId", $order->toArray());
            return null;
        }

        return $order->status;
    }

    /**
     * @param $userId
     * @param $orderId
     * @param $receipt
     * @return bool
     */
    public static function appleReceipt($userId, $orderId, $receipt): bool
    {
        \Log::info("apple pay query receipt", $receipt);

        $order = OrderModel::findOrEmpty($orderId);
        if ($order->isEmpty()
            || $order->userId != $userId
            || $order->status != OrderConfig::ORDER_STATUS_PAYING
            || $order->deleted == OrderConfig::ORDER_DELETED_YES
        ){
            \Log::error("order error on appleQuery userId:$userId, orderId:$orderId", $order->toArray());
            return false;
        }

        $md5 = md5($receipt);
        $record = IapReceiptModel::where('md5', $md5)->findOrEmpty();
        if(!$record->isEmpty()){
            \Log::error("appleQuery receipt exist userId:$userId, orderId:$orderId md5:$md5");
            return false;
        }
        $record->md5 = $md5;
        $record->receipt = $receipt;

        /*$ret = (new ApplePayService())->query($receipt);
        if(!$ret){
            \Log::error("apple receipt query error");
            return false;
        }
        list($tradeno, $paidTime) = $ret;*/
        list($tradeno, $paidTime) = ['iap'.time(), time()];

        return self::deal($order->sn, $order->price, $tradeno, $paidTime,
            function() use($record){
                return $record->save();
            });
    }

    /**
     * @param $userId
     * @param $orderId
     * @return bool
     */
    public static function cancel($userId, $orderId): bool
    {
        $order = OrderModel::findOrEmpty($orderId);
        if ($order->isEmpty()
            || $order->userId != $userId
            || ($order->status != OrderConfig::ORDER_STATUS_PAYING
                && $order->status != OrderConfig::ORDER_STATUS_CREATE)
            || $order->deleted == OrderConfig::ORDER_DELETED_YES
        ){
            \Log::error("order error on cancelOrder: userId:$userId, orderId:$orderId", $order->toArray());
            return false;
        }
        $order->status = OrderConfig::ORDER_STATUS_CANCELLED;
        if(!$order->save()){
            \Log::error("save order error on cancelOrder: userId:$userId, orderId:$orderId", $order->toArray());
            return false;
        }
        return true;
    }

    /*
     * 删除订单
     */
    public static function delete($userId, $orderId){
        $order = OrderModel::findOrEmpty($orderId);
        if ($order->isEmpty()
            || $order->userId != $userId
            || ($order->status != OrderConfig::ORDER_STATUS_PAID
                && $order->status != OrderConfig::ORDER_STATUS_REFUNDED
                && $order->status != OrderConfig::ORDER_STATUS_CANCELLED)
            || $order->deleted == OrderConfig::ORDER_DELETED_YES
        ){
            \Log::error("order error on deleteOrder: userId:$userId, orderId:$orderId", $order->toArray());
            return false;
        }
        $order->deleted = OrderConfig::ORDER_DELETED_YES;
        if(!$order->save()){
            \Log::error("save order error on deleteOrder: userId:$userId, orderId:$orderId", $order->toArray());
            return false;
        }
        return true;
    }

    /**
     * @param $userId
     * @param $page
     * @return array
     */
    public static function list($userId): array
    {
        return OrderModel::list($userId);
    }


}